/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#include "scenenode.h"
#include "camera.h"
#include "movableobject.h"
#include "functors.h"

#include <algorithm>

#ifdef _DEBUG
#ifdef _MSC_VER
   #define new new( _CLIENT_BLOCK, __FILE__, __LINE__)
#endif
#endif // _DEBUG

namespace Engine
{
	/**********************************************************************************************************/
	SceneNode::SceneNode(const std::string& name)
		:m_name(name)
		,m_parent(NULL)
		,m_transform(Matrix4<REAL>::IDENTITY)
		,m_derivedTransform(Matrix4<REAL>::IDENTITY)
	{
	}
	/**********************************************************************************************************/
	SceneNode::~SceneNode()
	{
		DetachAllObjects();
	}
	/**********************************************************************************************************/
	const std::string& SceneNode::GetName()const
	{
		return m_name;
	}
	/**********************************************************************************************************/
	bool SceneNode::CheckChildName(const std::string& name)
	{
		return m_childs.find(name) == m_childs.end();
	}
	/**********************************************************************************************************/
	SceneNodePtr SceneNode::AddChildNode(SceneNode* node)
	{
		if(node != 0)
		{
			node->m_parent = this;
			m_childs.insert(ChildsMap::value_type(node->GetName(), RefPtr<SceneNode, WeakPtrExtension>(node)));
			return m_childs[node->GetName()];
		}
		return SceneNodePtr();
	}
	/**********************************************************************************************************/
	SceneNodePtr SceneNode::CreateChild(const std::string& name)
	{
		if(!CheckChildName(name))
			throw std::invalid_argument("SceneNode with same name already exists " + name);

		SceneNode* node =  new SceneNode(name);
		return AddChildNode(node);
	}
	/**********************************************************************************************************/
	void SceneNode::DeleteChild(const std::string& name)
	{
		ChildsMap::iterator i = m_childs.find(name);
		if(i != m_childs.end())
			m_childs.erase(i);
	}
	/**********************************************************************************************************/
	void SceneNode::DeleteAllChilds()
	{
		m_childs.clear();
	}
	/**********************************************************************************************************/
	void SceneNode::Move(const Vector3<REAL>& pos)
	{
		Matrix4<REAL> matr;
		matr.MakeTrans(pos);
		m_transform = m_transform * matr;
	}
	/**********************************************************************************************************/
	void SceneNode::Rotate(const Vector3<REAL>& pos, REAL angle)
	{
		Matrix4<REAL> matr;
		matr.MakeRotFromAxis(pos,angle);
		m_transform = m_transform * matr;
	}
	/**********************************************************************************************************/
	void SceneNode::Scale(const Vector3<REAL>& scale)
	{
		Matrix4<REAL> matr;
		matr.MakeScale(scale);
		m_transform = m_transform * matr;
	}
	/**********************************************************************************************************/
	void SceneNode::SetTransform(const Matrix4<REAL>& trans)
	{
		m_transform = trans;
	}
	/**********************************************************************************************************/
	const Matrix4<REAL>& SceneNode::GetTransform() const
	{
		return m_transform;
	}
	/**********************************************************************************************************/
	const Matrix4<REAL>& SceneNode::GetDerivedTransform() const
	{
		return m_derivedTransform;
	}
	/**********************************************************************************************************/
	void SceneNode::Update()
	{
		UpdateSignal();
		if(m_parent != NULL)
			m_derivedTransform = m_transform * m_parent->GetDerivedTransform();
		// root
		else
			m_derivedTransform = m_transform;

		//update  childs		
		for_each(m_childs.begin(), m_childs.end(), on2nd(m_fn(&SceneNode::Update)));

		UpdateBounds();
	}
	/**********************************************************************************************************/
	void SceneNode::UpdateBounds()
	{
		m_boundingBox.Reset();

		//update with attached objects
        MovableObjectVector::iterator o, oe = m_movableObjects.end();
        for (o = m_movableObjects.begin(); o != oe; ++o)
        {
			(*o)->Update();
			m_boundingBox.Merge((*o)->GetWorldBoundingBox());
        }

		//update with childs
		for(ChildsMap::iterator i = m_childs.begin(), e = m_childs.end();i != e; ++i)
		{
			m_boundingBox.Merge(i->second->GetBoundingBox());
		}		
	}
	/**********************************************************************************************************/
	const AABB& SceneNode::GetBoundingBox()const
	{
		return m_boundingBox;
	}
	/**********************************************************************************************************/
	void SceneNode::AttachObject(MovableObjectPtr obj)
	{
		m_movableObjects.push_back(obj);
		obj->NotifyAttached(this);
	}
	/**********************************************************************************************************/
	void SceneNode::DetachObject(MovableObjectPtr obj)
	{
		MovableObjectVector::iterator i = std::find(m_movableObjects.begin(), m_movableObjects.end(), obj);
		if(i != m_movableObjects.end())
		{
			(*i)->NotifyDetached();
			m_movableObjects.erase(i);
		}
	}
	/**********************************************************************************************************/
	void SceneNode::DetachObject(const std::string& name)
	{
		MovableObjectVector::iterator i = m_movableObjects.begin(), e = m_movableObjects.end();
		for(;i != e; ++i)
		{
			if((*i)->GetName() == name)
			{
				(*i)->NotifyDetached();
				m_movableObjects.erase(i);
				break;
			}
		}
	}
	/**********************************************************************************************************/
	void SceneNode::DetachAllObjects()
	{
		MovableObjectVector::iterator i = m_movableObjects.begin(), e = m_movableObjects.end();
		for(;i != e; ++i)
		{
			if(!(*i).IsNull())
				(*i)->NotifyDetached();
		}
		m_movableObjects.clear();
	}
	/**********************************************************************************************************/
	void SceneNode::FindVisibleObjects(const Camera* camera, RenderQueue* renderqueue, RenderQueue* shadowsQueue, const std::vector<LightPtr>& lightList)
	{
		Visibility vis = camera->GetVisibility( GetBoundingBox());
		if(vis == NONE)
			return;

		FindVisibleMovableObjects(camera, renderqueue,shadowsQueue,lightList,vis);

		//find childs
		for(ChildsMap::iterator i = m_childs.begin(), e = m_childs.end();i != e; ++i)
		{
			i->second->FindVisibleObjects(camera, renderqueue,shadowsQueue,lightList);
		}

	}
	/**********************************************************************************************************/
	void SceneNode::FindVisibleMovableObjects(const Camera* camera, RenderQueue* renderqueue, RenderQueue* shadowsQueue, const std::vector<LightPtr>& lightList, Visibility vis)
	{
		Vector3<REAL> pos(GetDerivedTransform()[0][3],GetDerivedTransform()[1][3],GetDerivedTransform()[2][3]);

		//find attached objects
		MovableObjectVector::iterator o, oe = m_movableObjects.end();
        for (o = m_movableObjects.begin(); o != oe; ++o)
        {
			if((*o)->GetRenderingDistance() <= (camera->GetPosition() - pos).Length())
			{
				if(vis == FULL || camera->GetVisibility((*o)->GetWorldBoundingBox()) != NONE)
					(*o)->UpdateRenderQueue(renderqueue,shadowsQueue,lightList);
			}
        }
	}
	/**********************************************************************************************************/
}
